package com.uraurora.crescent.function

import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.schedulers.Schedulers
import org.junit.Before
import org.junit.Test
import java.io.File
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Paths

@JvmOverloads
fun <T> Iterable<T>.join(prefix: String="<", sep: String=",", suffix: String=">") : String{
    val itr = this.iterator()
    return buildString {
        append(prefix)
        while (itr.hasNext()){
            append(itr.next())
            if(itr.hasNext()){
                append(sep)
            }
        }
        append(suffix)
    }
}

class KtTest {

    @Test fun fileTest(){
        val files = File("/").walk()
                .maxDepth(2)
                .filter { it.isFile }
                // .filter { it.extension in listOf("txt", "mp4", "avi") }
                .map { it.name }
                .toList()
        files.forEach { println(it) }
        Files.newBufferedReader(Paths.get("/"), StandardCharsets.UTF_8)
                .use {  }
    }

    /**
     * lambda测试
     */
    @Test
    fun first(){
        generateSequence(1) { 1+it }
                .map { it*2 }
                .take(12)
                .forEach(::print)
        listOf(1, 2, 3, 4, 5).asSequence()
                .map { it+1 }
                .take(3)
                .forEach(::print)
    }

    /**
     * 命名参数与重载测试
     */
    @Test
    fun second(){
        print(joinToString(nums = listOf(1, 2, 15)))
    }

    @JvmOverloads
    fun joinToString(nums: List<Int>, prefix: String = "<", sep: String = ",", postfix: String = ">"): String {
        return buildString {
            append(prefix)
            for((index, element) in nums.withIndex()){
                append(element)
                if (index < nums.size-1) append(sep)
            }
            append(postfix)
        }
    }


    /**
     * 类委托测试
     */
    @Test
    fun third() {
        val cout = CountingSet<Int>()
        println(if (cout.addAll(listOf(1, 2, 3))) cout.count else 0)
    }

    /**
     * apply构建测试
     */
    @Test
    fun fourth() {
        Person().buildPerson {
            for(i in 'A'..'F'){
                addAddress(i.toString())
            }
        }
                .address.asSequence()
                .filter { it in listOf("A", "F") }
                .forEach { println(it) }
    }
    @ExperimentalStdlibApi
    @Test
    fun fifth(){
        val p1 = Point.getInstance(1, 4); val p2 = Point.getInstance(3, 7)
        println(p1 + p2)

        println(Point(3, 4) in Point(1, 2)..Point(5, 6))
    }

    @Test
    fun sixth(){
        mapOf(1 to "1", 2 to "2")
        val keys = 'a'..'g'
        keys.associateWith { it.plus("value:$it") }
        println(keys.chunked(3))

    }

    @Test fun seventh(){
        var res = mutableListOf<String>()
        val l = 1..90
        Observable.fromIterable(l)
                .doOnSubscribe { println("first doOn subscribe:" + Thread.currentThread().name) }
                .map {
                    println("first map:" + Thread.currentThread().name)
                    it * 2
                }
                .observeOn(Schedulers.newThread())
                .map {
                    println("second map:" + Thread.currentThread().name)
                    it.toString().plus(",test")
                }
                .subscribeOn(Schedulers.single())
                .doOnNext { println("doOn next:" + Thread.currentThread().name) }
                .observeOn(Schedulers.io())
                .doOnSubscribe { println("second doOn subscribe:" + Thread.currentThread().name) }
                .map {
                    println("third map:" + Thread.currentThread().name)
                    it.toString()
                }
                .subscribe{
                    println("$it,subscribe:" + Thread.currentThread().name)
                    res.add(it)
                }
        println(res)
    }

    @Test fun eighth(){
        println(listOf(1, 2, 4).join("<", ",", ">"))
    }

    @Test fun observableTest(){
        val res = mutableListOf<Int>();
        val records = Service.getResponse()?.records ?: listOf()
        val map = Observable.just(records)
                .map { it.associateBy({ it?.name ?: "" }, { it }) }
                .observeOn(Schedulers.newThread())
                // .subscribe { print(Thread.currentThread().name + it) }
        Observable.fromIterable(records)
                .groupBy{it?.name?:""}
                .subscribe { print(it) }

    }

}

class CountingSet<T>(
        val set: MutableSet<T> = HashSet())
    : MutableSet<T> by set{
    var count = 0

    override fun add(element: T): Boolean {
        count++
        return set.add(element)
    }

    override fun addAll(elements: Collection<T>): Boolean {
        count+=elements.size
        return set.addAll(elements)
    }
}

class Person(
        val name: String = "someone",
        val address: MutableList<String> = mutableListOf()
) : Comparable<Person>{
    override fun compareTo(other: Person): Int {
        return if (name.length>other.name.length) 1 else -1
    }

    fun addAddress(address: String): Boolean{
        this.address.add(address)
        return true
    }

    fun buildPerson(action: Person.() -> Unit): Person{
        this.apply(action)
        return this
    }
}

data class Point(
        val x: Int,
        val y: Int
) : Comparable<Point>{
    companion object Cache{
        private const val size = 10

        @ExperimentalStdlibApi
        private val pointPool = buildList {
            for(i in -10..10){
                val l = buildList {
                    for (j in -10..10){
                        add(Point(i, j))
                    }
                }
                add(l)
            }
        }

        fun newInstance(x: Int, y: Int) :Point{
            return Point(x, y)
        }

        @ExperimentalStdlibApi
        fun getInstance(x: Int, y: Int) :Point{
            return pointPool[x + size][y + size]
        }
    }

    override fun compareTo(other: Point): Int {
        return if (this == other) 0 else
            (if(x>other.x && y> other.y) 1 else -1)
    }

    operator fun plus(other: Point): Point{
        return Point(x + other.x, y + other.y)
    }

}

object Service{
    fun getResponse():Response?{
        return Response().apply {
            records?.addAll(listOf(Record(), Record(name = "tom"), Record(name = "tom", address = "北京")))
        }
    }

    fun multiGet(keys: List<StoreKey>): Map<StoreKey, String>{
        return keys.associateWith { it.toString() }
    }
}

data class Record(val name:String? = null, val city:Int = 1, val address:String? = null)

data class Response(val status:Status = Status.OK, val records:MutableList<Record?>? = mutableListOf())

enum class Status{
    OK, FAILED
}

@Test fun dataTest(){
    1 to 1
    val response = Service.getResponse()
    val records = if (response?.status == Status.OK) response.records else mutableListOf()
}

data class StoreKey(val category: String, val o: List<Any>)

class MyClass{
    private lateinit var myService : Service

    @Before fun setUp(){
        myService = Service
    }
}
